// 使用require方式导入ali-oss
// import * as OSS from 'ali-oss'
const OSS = require('ali-oss');
import { cancelUploadBigFile, reportUploadError, getSTSToken, reportBigFileUrl } from '@/services/upload'

// 自定义错误接口
interface CustomError extends Error {
  code?: string
  // 添加错误上报标记
  _reported?: boolean
}

// 格式化工具函数
const formatSpeed = (speed: number): string => {
  if (speed < 1024) return `${speed.toFixed(1)} B/s`
  if (speed < 1024 * 1024) return `${(speed / 1024).toFixed(1)} KB/s`
  return `${(speed / 1024 / 1024).toFixed(1)} MB/s`
}

const formatTime = (seconds: number): string => {
  if (seconds < 60) return `${Math.ceil(seconds)}s`
  const hours = Math.floor(seconds / 3600)
  const minutes = Math.floor((seconds % 3600) / 60)
  const remainingSeconds = Math.ceil(seconds % 60)

  let result = ''
  if (hours > 0) result += `${hours}h`
  if (minutes > 0) result += `${minutes}m`
  result += `${remainingSeconds}s`
  return result
}

interface VideoMetadata {
  duration: number
  width: number
  height: number
}

const getVideoMetadata = (file: File | Blob | string): Promise<VideoMetadata> => {
  return new Promise((resolve, reject) => {
    const video = document.createElement('video')
    let url = ''

    try {
      if (typeof file === 'string') {
        url = file
      } else if (file instanceof File || file instanceof Blob) {
        url = URL.createObjectURL(file)
      } else {
        throw new Error('Invalid file type')
      }

      video.onloadedmetadata = () => {
        resolve({
          duration: video.duration,
          width: video.videoWidth,
          height: video.videoHeight,
        })
        if (url !== file) {
          URL.revokeObjectURL(url)
        }
      }

      video.onerror = e => {
        if (url !== file) {
          URL.revokeObjectURL(url)
        }
        reject(new Error(`Video metadata could not be loaded: ${e}`))
      }

      video.src = url
    } catch (error) {
      reject(new Error(`Failed to process video file: ${error}`))
    }
  })
}

// 获取图片的宽高
const getImageSize = (
  file: File | Blob | string | any,
): Promise<{ width: number; height: number }> => {
  return new Promise((resolve, reject) => {
    const img = new Image()
    let url = ''
    try {
      if (typeof file === 'string') {
        url = file
      } else if (file instanceof File || file instanceof Blob) {
        url = URL.createObjectURL(file)
      } else if (typeof file === 'object') {
        url = URL.createObjectURL(file.file)
      } else {
        throw new Error('Invalid file type')
      }

      img.onload = () => {
        resolve({ width: img.width, height: img.height })
      }

      img.onerror = () => {
        if (url !== file) {
          URL.revokeObjectURL(url)
        }
        reject(new Error('Failed to load image'))
      }

      img.src = url
    } catch (error) {
      reject(new Error(`Failed to process image file: ${error}`))
    }
  })
}

export interface UploadStatus {
  id?: string
  fileName?: string
  status: 'PENDING' | 'UPLOADING' | 'SUCCESS' | 'FAIL'
  progress: number
  speed: string
  remainingTime: string
  url?: string
  cover?: string
  width?: number
  height?: number
  duration?: number
  error?: string
  previewUrl?: string
  file?: File
}

export const useOSSUploader = () => {
  // 取消分片上传
  const cancelUploadPart = async (uploadRequestId: string, client?: OSS, checkpoint?: any) => {
    try {
      // 如果有checkpoint信息，优先使用OSS客户端中止上传
      if (client && checkpoint && checkpoint.name && checkpoint.uploadId) {
        try {
          console.log('通过OSS客户端中止分片上传', checkpoint.name, checkpoint.uploadId)
          await client.abortMultipartUpload(checkpoint.name, checkpoint.uploadId)
          console.log('分片上传已成功中止')
        } catch (abortErr) {
          console.error('OSS客户端中止分片上传失败', abortErr)
        }
      }

      // 调用服务端接口取消上传
      await cancelUploadBigFile({
        uploadRequestId,
      })
    } catch (error) {
      console.error('取消分片上传失败', error)
    }
  }

  // 上报错误信息
  const reportUploadErrorMsg = async (
    file: File,
    err: CustomError,
    stsResponse: any,
    client?: OSS,
    checkpoint?: any,
  ) => {
    try {
      // 如果已经上报过，不再重复上报
      if (err._reported) {
        console.log('错误已上报，跳过重复上报')
        return
      }
      await reportUploadError({
        // domain: info.value?.creatorDomain,
        errorCode: err?.code || err.name,
        errorMsg: err.message,
        bucketName: stsResponse?.bucketName,
        objectKey: stsResponse?.objectKey,
        fileSize: file.size,
        uploadRequestId: stsResponse?.uploadRequestId,
      })

      // 标记该错误已经上报
      err._reported = true

      if (stsResponse?.uploadRequestId) {
        cancelUploadPart(stsResponse.uploadRequestId, client, checkpoint)
      }
    } catch (error) {
      console.error('上报错误信息失败', error)
    }
  }

  const upload = async (
    param: any,
    onSuccess?: (result: {
      url: string
      videoUrl?: string
      coverBlurPic?: string
      coverPicUrl?: string
      width?: number
      height?: number
      duration?: number
      type?: number
    }) => void,
    onProgress?: (progress: number) => void,
    onError?: (error: Error) => void,
    onSpeedChange?: (speed: string) => void,
    onRemainingTimeChange?: (time: string) => void,
  ) => {
    const { file, uploadType } = param
    let stsResponse: any = null
    try {
      const stsRes = await getSTSToken({
        fileName: file.file.name,
        totalSize: file.file.size,
        fileType: uploadType,
      })
      stsResponse = stsRes.data
      let _duration = 0
      let _width = 0
      let _height = 0
      if (uploadType === 2) {
        // 获取视频时长
        const { duration, width, height } = await getVideoMetadata(file.file)
        _duration = duration
        _width = width
        _height = height
      } else if (uploadType === 1) {
        // 获取图片宽高
        const { width, height } = await getImageSize(file.file)
        _width = width
        _height = height
      }
      if (stsResponse?.url) {
        onSuccess?.({
          url: stsResponse.url,
          videoUrl: stsResponse?.url,
          coverBlurPic: stsResponse?.coverBlurPic,
          coverPicUrl: stsResponse?.cover,
          width: _width,
          height: _height,
          duration: _duration,
          type: uploadType,
        })
        onProgress?.(100)
        return
      }

      console.log(OSS, 'stsResponse', stsResponse)

      // 修改OSS客户端实例化方式
      const client = OSS({
        region: stsResponse.region,
        accessKeyId: stsResponse.credentials.accessKeyId,
        accessKeySecret: stsResponse.credentials.accessKeySecret,
        bucket: stsResponse.bucketName,
        stsToken: stsResponse.credentials.securityToken,
        endpoint: stsResponse.endpoint,
        secure: true,
        timeout: 180000,
      })

      console.log('client', client)

      // 分片上传状态记录
      const partsStatus: {
        total: number
        completed: number
        failed: number
        retrying: boolean
        checkpoint?: {
          name: string
          uploadId: string
          fileSize: number
          doneParts: any[]
          [key: string]: any
        } | null
      } = {
        total: 0, // 总分片数
        completed: 0, // 已完成分片数
        failed: 0, // 失败分片数（包括已重试的）
        retrying: false, // 是否正在重试
        checkpoint: null, // 保存checkpoint用于可能的中止操作
      }

      const startTime = Date.now()
      let lastLoaded = 0
      let lastTime = startTime

      // 添加速度平滑计算所需的变量
      const speedSamples: number[] = []
      const MAX_SPEED_SAMPLES = 5 // 保留最近5个样本进行平均
      let prevAvgSpeed = 0 // 保存前一次的平均速度，用于进一步平滑

      const uploadOptions = {
        parallel: 2,
        partSize: stsResponse.fileUploadPartSize * 1024 * 1024,
        retryCount: 0,
        retryMax: 3,
        progress: (p: number, checkpoint: any) => {
          // 记录总分片数
          if (checkpoint && checkpoint.doneParts) {
            partsStatus.total = checkpoint.fileSize / uploadOptions.partSize + 1
            partsStatus.completed = checkpoint.doneParts.length
            // 保存checkpoint用于可能的中止操作
            partsStatus.checkpoint = checkpoint
          }

          const currentTime = Date.now()
          const timeElapsed = (currentTime - lastTime) / 1000
          const loaded = p * file.file.size

          // 防止时间间隔过小导致计算不准确
          if (timeElapsed < 0.1) {
            return // 忽略过快的更新
          }

          const instantSpeed = (loaded - lastLoaded) / timeElapsed

          // 过滤异常值，如果速度突然增大或减小太多，可能是网络抖动
          const isValidSpeed = instantSpeed > 0

          if (isValidSpeed) {
            // 使用移动平均计算平滑的速度
            speedSamples.push(instantSpeed)
            // 只保留最近的几个样本
            if (speedSamples.length > MAX_SPEED_SAMPLES) {
              speedSamples.shift()
            }
          }

          // 计算平均速度
          const avgSpeed =
            speedSamples.length > 0
              ? speedSamples.reduce((sum, speed) => sum + speed, 0) / speedSamples.length
              : instantSpeed

          // 进一步平滑处理：新速度和旧速度的加权平均
          // 调整权重，给予新速度更高的权重 (例如 0.5)
          const smoothedSpeed = prevAvgSpeed === 0 ? avgSpeed : avgSpeed * 0.5 + prevAvgSpeed * 0.5

          prevAvgSpeed = smoothedSpeed

          const remainingBytes = file.file.size - loaded
          const remainingTime = smoothedSpeed > 0 ? remainingBytes / smoothedSpeed : 0

          // 调用各个回调
          onProgress?.(Math.floor(p * 100))
          onSpeedChange?.(formatSpeed(smoothedSpeed))
          onRemainingTimeChange?.(formatTime(Math.ceil(remainingTime)))

          lastLoaded = loaded
          lastTime = currentTime
        },
        meta: {
          uid: Date.now(),
          pid: Date.now(),
        },
        mime: file.file.type,
        headers: {
          'Cache-Control': 'no-cache',
          'Content-Disposition': `attachment; filename=${encodeURIComponent(file.file.name)}`,
        },
      }

      const multipartUploadWithRetry = async () => {
        try {
          // 如果有checkpoint，说明之前有上传过部分分片
          if (partsStatus.checkpoint && partsStatus.checkpoint.uploadId) {
            // 获取已上传的分片列表
            const { parts } = await client.listParts(
              partsStatus.checkpoint.name,
              partsStatus.checkpoint.uploadId,
            )

            // 找出未完成的分片
            const uploadedPartNumbers = new Set(parts.map(part => part.PartNumber))
            const totalParts = Math.ceil(file.file.size / uploadOptions.partSize)
            const remainingParts = Array.from({ length: totalParts }, (_, i) => i + 1).filter(
              partNumber => !uploadedPartNumbers.has(partNumber),
            )

            if (remainingParts.length > 0) {
              console.log(`继续上传剩余 ${remainingParts.length} 个分片`)
              // 继续上传剩余分片
              return await client.multipartUpload(partsStatus.checkpoint.name, file.file, {
                ...uploadOptions,
                checkpoint: {
                  ...partsStatus.checkpoint,
                  file: file.file,
                  partSize: uploadOptions.partSize,
                },
              })
            }
          }

          // 如果没有checkpoint或所有分片都已上传，则开始新的上传
          return await client.multipartUpload(stsResponse.objectKey, file.file, uploadOptions)
        } catch (err) {
          const error = err as CustomError
          console.error('分片上传出错：', error.name, error.message, error.code)

          // 尝试中止分片上传
          if (partsStatus.checkpoint) {
            try {
              console.log(
                '尝试中止分片上传',
                partsStatus.checkpoint.name,
                partsStatus.checkpoint.uploadId,
              )
              await client.abortMultipartUpload(
                partsStatus.checkpoint.name,
                partsStatus.checkpoint.uploadId,
              )
              console.log('分片上传已成功中止')
            } catch (abortErr) {
              console.error('中止分片上传失败', abortErr)
            }
          }

          // 尝试处理所有可能的网络错误
          const isNetworkError =
            error.name === 'PartNotSequentialError' ||
            error.name === 'ConnectionTimeoutError' ||
            error.name === 'RequestError' ||
            error.name === 'RequestTimeout' ||
            error.name === 'NetworkError' ||
            error.code === 'ERR_CONNECTION_RESET' ||
            error.code === 'ERR_NETWORK' ||
            error.message?.includes('upload part error') ||
            error.message?.includes('NetworkingError') ||
            error.message?.includes('net::ERR_CONNECTION_RESET') ||
            error.message?.includes('connected: false') ||
            error.message?.includes('keepalive socket: false') ||
            error.message?.includes('timeout')

          if (isNetworkError) {
            if (uploadOptions.retryCount < uploadOptions.retryMax) {
              // 增加重试计数
              uploadOptions.retryCount++
              partsStatus.retrying = true
              partsStatus.failed++

              // 添加重试延迟，避免立即重试可能再次失败
              const retryDelay = Math.min(1000 * Math.pow(2, uploadOptions.retryCount - 1), 10000) // 指数退避策略，最大10秒
              console.warn(
                `分片上传失败，将在 ${retryDelay / 1000} 秒后进行第 ${uploadOptions.retryCount} 次重试...`,
              )
              onSpeedChange?.('Retrying...')

              // 等待一段时间后重试
              await new Promise(resolve => setTimeout(resolve, retryDelay))
              partsStatus.retrying = false

              // 递归重试：再次调用 multipartUploadWithRetry
              return await multipartUploadWithRetry()
            } else {
              // 超过最大重试次数，上报错误
              console.error(`分片上传失败，已重试${uploadOptions.retryMax}次，上报错误`)
              partsStatus.retrying = false
              reportUploadErrorMsg(file.file, error, stsResponse, client, partsStatus.checkpoint)
              throw error
            }
          } else {
            // 其他错误直接上报并抛出
            reportUploadErrorMsg(file.file, error, stsResponse, client, partsStatus.checkpoint)
            throw error
          }
        }
      }

      // 初始调用
      const result = await multipartUploadWithRetry()

      const url = (result.res as any).requestUrls?.[0]?.split('?')[0] || ''

      // 上报文件上传信息
      const res = await reportBigFileUrl({
        fileUrl: url,
        fileType: uploadType,
        uploadRequestId: stsResponse.uploadRequestId,
      })

      // 处理响应数据
      let resultUrl = '';
      let coverBlurPic = '';
      let coverPic = '';

      // 检查响应格式
      if (res && res.data) {
        // 使用data属性获取响应内容
        resultUrl = res.data.fileUrl || url;
        coverBlurPic = res.data.coverBlurPic || '';
        coverPic = res.data.cover || '';
      } else if (res && typeof res === 'object') {
        // 直接从响应对象获取
        resultUrl = res.fileUrl || url;
        coverBlurPic = res.coverBlurPic || '';
        coverPic = res.cover || '';
      }

      if (resultUrl) {
        // 上传成功回调
        onSuccess?.({
          url: resultUrl,
          videoUrl: resultUrl,
          coverBlurPic: coverBlurPic,
          coverPicUrl: coverPic,
          width: _width,
          height: _height,
          duration: _duration,
          type: uploadType,
        })
      } else {
        // 上传失败回调
        reportUploadErrorMsg(
          file.file,
          new Error('upload failed') as CustomError,
          stsResponse,
          client,
          partsStatus.checkpoint,
        )
        onError?.(new Error('upload failed'))
      }
    } catch (error) {
      const err = error as CustomError
      // 上报错误信息，在外层错误处理中不能访问client和partsStatus
      reportUploadErrorMsg(file.file, err, stsResponse)
      onError?.(err)
      throw err
    }
  }

  return { upload, cancelUploadPart, formatTime, getVideoMetadata, getImageSize }
}
